﻿module AstProcessor

 (* Goes over an AST and breaks all nested assignment commands to a sequence of regular assignment commands.
    All other command types remain unchanged. *)
let astBreakNestedExpressions(ast) =

    (* breaks an assignment command into a sequence of assignment commands which are semantically identical
       in term of effect on the store *)
    let breakAsgnNestedExpression(c : Core.Cmd, newVar : int) : Core.Cmd =
        let rec bne(c : Core.Cmd) : Core.Cmd =
            match c with
                | Core.Asgn(i, Core.Plus(Core.Zero, e)) -> bne(Core.Asgn(i, e))
                | Core.Asgn(i, Core.Plus(e, Core.Zero)) -> bne(Core.Asgn(i, e))
                | Core.Asgn(i, Core.Plus(e, Core.Var y)) -> Core.Seq(bne(Core.Asgn(i,e)), Core.Asgn(i,Core.Plus(Core.Var i,Core.Var y)))
                | Core.Asgn(i, Core.Plus(Core.Var y, e)) -> Core.Seq(bne(Core.Asgn(i,e)), Core.Asgn(i,Core.Plus(Core.Var i,Core.Var y)))
                | Core.Asgn(i, Core.Times(e, Core.Var y)) -> Core.Seq(bne(Core.Asgn(i,e)), Core.Asgn(i,Core.Times(Core.Var i,Core.Var y)))
                | Core.Asgn(i, Core.Times(Core.Var y, e)) -> Core.Seq(bne(Core.Asgn(i,e)), Core.Asgn(i,Core.Times(Core.Var i,Core.Var y)))

                | Core.Asgn(i, Core.Times(e1, e2)) -> Core.Seq( Core.Seq( bne(Core.Asgn(i,e1)), bne(Core.Asgn(i+1,e2))),
                                                             Core.Asgn(i,Core.Times(Core.Var i, Core.Var (i+1))))
                | Core.Asgn(i, Core.Plus(e1, e2)) -> Core.Seq( Core.Seq( bne(Core.Asgn(i,e1)),bne(Core.Asgn(i+1,e2))),
                                                              Core.Asgn(i,Core.Plus(Core.Var i, Core.Var (i+1))))
                | _ -> c

        match c with
            | Core.Asgn(i,Core.Plus(Core.Zero, Core.Var var))
            | Core.Asgn(i,Core.Plus(Core.Var var, Core.Zero)) -> Core.Asgn(i, Core.Var var)
            | Core.Asgn(i,Core.Times(Core.Zero, _))
            | Core.Asgn(i,Core.Times(_, Core.Zero)) -> Core.Asgn(i, Core.Zero)

            | Core.Asgn(i,Core.Zero)
            | Core.Asgn(i,Core.Var _)
            | Core.Asgn(i,Core.Plus(Core.Var _, Core.Var _))
            | Core.Asgn(i,Core.Times(Core.Var _, Core.Var _)) -> c

            | Core.Asgn(i,e) -> Core.Seq(bne(Core.Asgn(newVar,e)),Core.Asgn(i,Core.Var newVar))

            | _ -> c

    (* goes over an AST and breaks all nested assignment commands to a sequence of regular assignment commands.
      'hv' is a variable index greater than all variable indices referenced in any of the handled expressions. *)
    let rec breakNestedExpressions = function
        | (Core.Skip, lastVarId) -> Core.Skip
        | (Core.Seq(c1, c2), lastVarId) -> Core.Seq(breakNestedExpressions(c1, lastVarId),breakNestedExpressions(c2, lastVarId))
        | (Core.Loop(i, c), lastVarId) -> Core.Loop(i,breakNestedExpressions(c, lastVarId))
        | (Core.Choice(c1, c2), lastVarId) -> Core.Choice(breakNestedExpressions(c1, lastVarId),breakNestedExpressions(c2, lastVarId))
        | (cmd, lastVarId) -> breakAsgnNestedExpression(cmd, lastVarId);

    breakNestedExpressions(ast, Core.NumOfVars(ast) + 1)